------------------------------------------------------------------------------
-- HTTP server module
--
-- LICENCE: http://opensource.org/licenses/MIT
-- Vladimir Dronnikov <dronnikov@gmail.com>
------------------------------------------------------------------------------
local collectgarbage, tonumber, tostring = collectgarbage, tonumber, tostring
local srv = nil
local funcs = {}

------------------------------------------------------------------------------
-- request methods
------------------------------------------------------------------------------
local make_req = function(conn, method, url)
  return {
    conn = conn,
    method = method,
    url = url,
    header = {},
    data = nil
  }
end

------------------------------------------------------------------------------
-- response methods
------------------------------------------------------------------------------
local make_res = function(csend, cfini)
  local send = function(self, code, data, status)

    csend("HTTP/1.1 ")
    csend(tostring(status or 200))
    csend(" OK\r\n")
    -- we use chunked transfer encoding, to not deal with Content-Length:
    self:set_header("Transfer-Encoding", "chunked")
    self:set_header("Connection", "close")

    for name, value in pairs(self.header) do
      csend(name .. ": " .. value .. "\r\n")
    end
    csend("\r\n")  -- end response headers
    data = data or {}
    local str = cjson.encode({code = code, data = data })

    csend(("%X\r\n"):format(#str))
    csend(str)
    csend("\r\n")
    csend("0\r\n\r\n")
    cfini()
  end

  local set_header = function(self, name, value)
    self.header[name] = value
  end

  return {
    header = {},
    set_header = set_header,
    send = send,
  }
end

------------------------------------------------------------------------------
-- HTTP parser  每来一个新的tcp链接，就会调用这个方法
------------------------------------------------------------------------------
local http_handler = function(conn)
  local csend = (require "fifosock").wrap(conn)

  local req, res
  local buf = ""
  local method, url

  local ondisconnect = function(connection)
    connection:on("receive", nil)
    connection:on("disconnection", nil)
    connection:on("sent", nil)
    collectgarbage("collect")
  end

  local cfini = function()
    csend(function()
      conn:on("sent", nil)
      conn:close()
      ondisconnect(conn)
    end)
  end

  -- header parser
  local cnt_len = 0

  local onheader = function(_, k, v)
    if k == "content-length" then
      cnt_len = tonumber(v)
    end
    if k == "expect" and v == "100-continue" then
      csend("HTTP/1.1 100 Continue\r\n")
    end
    if req then
      req.header[k] = v
    end
  end

  -- body data handler
  local body_len = 0
  local all_data = ""
  local ondata = function(_, chunk)

    all_data = all_data .. chunk

    body_len = body_len + #chunk
    
    if body_len >= cnt_len then
      local req_body = cjson.decode(all_data)
      print(all_data)
      if funcs[req_body.act] then
        req.data = req_body.data
        funcs[req_body.act](req, res)
      else
        res:send(-1, "act not exit")
      end
    end
  end

  local onreceive = function(connection, chunk)
    -- merge chunks in buffer
    if buf then
      buf = buf .. chunk
    else
      buf = chunk
    end
    -- consume buffer line by line
    while #buf > 0 do
      -- 能找到\r\n的，认为是请求头部分
      local e = buf:find("\r\n", 1, true)
      if not e then break end
      local line = buf:sub(1, e - 1) -- 请求头中的一行
      buf = buf:sub(e + 2)
      -- method, url?
      if not method then
        do
          local _
          -- NB: just version 1.1 assumed
          _, _, method, url = line:find("^([A-Z]+) (.-) HTTP/1.1$")
        end
      
        if method then
          -- make request and response objects
          req = make_req(connection, method, url)
          res = make_res(csend, cfini)
        end

      elseif #line > 0 then
        -- parse header
        local _, _, k, v = line:find("^([%w-]+):%s*(.+)")
        if k then
          k = k:lower()
          onheader(connection, k, v)
        end
      else
        -- NB: we explicitly reassign receive handler so that
        --   next received chunks go directly to body handler
        connection:on("receive", ondata)
        -- NB: we feed the rest of the buffer as starting chunk of body
        ondata(connection, buf)
        -- buffer no longer needed
        buf = nil
        break
      end
    end
  end

  conn:on("receive", onreceive)
  conn:on("disconnection", ondisconnect)
  
end

local obj = {}

function obj:CreateServer(port)
  if srv then srv:close() end

  srv = net.createServer(net.TCP, 15)
  srv:listen(port, http_handler)
  print("http server start")
  return srv
end

function obj:SetAct(act, func)
  funcs[act] = func
end

return obj